github.com/quinndk/ethereum_read@v0.0.0-20181211143958-29c55eec3237/Docs/0xa0 EVM机制 .md (about) 1 # 0xa0 EVM机制 2 3 EVM,Ethereum Virtual Machine,以太坊虚拟机。它是以太坊智能合约的运行环境。我们知道之前我们写简单的智能合约时都需要将solidlity代码编译形成字节码才能够部署到以太坊上。同时在交易模块讲了一笔交易的大概流程,但是对于交易的真正执行并没有涉及到,其实交易的执行也是依赖于EVM。 4 5 # 原理 6 7 EVM本质上是一个堆栈机器,最直接的功能就是执行 智能合约。关于其定义,[官档](https://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html#index-6)给出的叙述是这样的: 8 9 > The Ethereum Virtual Machine or EVM is the runtime environment for smart contracts in Ethereum. It is not only sandboxed but actually completely isolated, which means that code running inside the EVM has no access to network, filesystem or other processes. Smart contracts even have limited access to other smart contracts. 10 以太坊虚拟机或EVM是以太坊中智能合约的运行时环境。 它不仅仅是沙箱,而且实际上是完全隔离的,这意味着EVM无法访问网络,文件系统或其他进程。 智能合约甚至可以限制其他智能合约的使用。 11 12 接着官档介绍了有关EVM的一些诸如Account,Gas等概念的介绍 ,都是之前接触过的在此略过不提。这里着重看一下EVM的存储系统和其他几个重要的概念。 13 14 ### 存储系统 15 EVM机器位宽为256位,即32个字节,256位机器字宽不同于我们经常见到主流的64位的机器字宽,这就表明EVM设计上将考虑一套自己的关于操作,数据,逻辑控制的指令编码。目前主流的处理器原生支持的计算数据类型有:8bits整数,16bits整数,32bits整数,64bits整数。 16 17 EVM中每个账户有一块持久化内存区称为 存储 。存储是将256位字映射到256位的键值存储区。 在合约中枚举存储是不可能的,且读存储的相对开销很高,修改存储的开销甚至更高。合约只能读写存储区内属于自己的部分。 18 19 第二个内存区域称为内存,合约每次调用会获取一块被清除确保没有脏数据的内存。存储器是线性的,可以在字节级读取,但读取限制为256位宽,而写操作可以是8位或256位宽。当访问(读取或写入)先前未访问过的存储器字(字内的任何偏移)时,存储器会按字(256位)进行扩展。扩容会消耗一定的Gas。随着内存的增大,内存成本越高(二次方指数增长)。 20 21 EVM不是基于寄存器的,而是基于栈机器,因此所有计算都在栈上执行。栈的容量为1024,每个元素是一个包含256位的字。可以将最顶部的16个元素之一复制到栈顶,或者将最顶层的元素与其下面的16个元素之一交换。所有其他操作只能从栈中取最顶部的两个(或一个或多个,取决于操作)元素进行运算,然后压栈道栈顶。 22 23 ### Instruction Set指令集 24 25 EVM的指令集量应尽量少,以最大限度地避免可能导致共识问题的错误实现。所有的指令都是针对”256位的字(word)”这个基本的数据类型来进行操作。具备常用的算术、位、逻辑和比较操作。也可以做到有条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。 26 27 ### Message Calls消息调用 28 29 合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源、目标、数据、以太币、gas和返回数据。事实上每个交易都由一个顶层消息调用组成,这个消息调用又可创建更多的消息调用。 30 31 合约可以决定在其内部的消息调用中,对于剩余的 gas ,应发送和保留多少。如果在内部消息调用时发生了out-of-gas异常(或其他任何异常),这将由一个被压入栈顶的错误值所指明。此时,只有与该内部消息调用一起发送的gas会被消耗掉。并且,Solidity中,发起调用的合约默认会触发一个手工的异常,以便异常可以从调用栈里“冒泡出来”。 32 33 如前文所述,被调用的合约(可以和调用者是同一个合约)会获得一块刚刚清空过的内存,并可以访问调用的payload——由被称为 calldata 的独立区域所提供的数据。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。 调用深度被 限制 为 1024 ,因此对于更加复杂的操作,我们应 使用循环而不是递归。 34 35 ### Delegatecall / Callcode and Libraries 委托调用和代码调用库 36 37 Delegatecall是EVM中一种特殊的消息调用,它与普通消息调用的区别在于:目标地址的代码将在发起调用的合约的上下文中执行,并且 msg.sender 和 msg.value 不变。这就意味着合约可以在运行时从不同的地址动态加载代码。存储、当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。 38 39 如此就使得Solidity实现"库调用"成为可能,于是就出现了可复用的代码调用库。 40 41 ### Logs日志 42 43 Logs是一直能够特殊的可索引的数据结构,其存储的数据可以一直映射到区块层级,Solidity借助它来实现事件(Events)。 44 45 智能合约一经创建就无法访问Logs,但Logs可以从区块链外有效地访问。部分Logs数据被存储在Bloom filter(布隆过滤器)中,因此可以以高效且加密的方式搜索此数据,也是因为这样那些没有下载全节点的轻客户端也能够访问这些数据。 46 47 ### Create & Self-destruct 智能合约的创建和销毁 48 49 智能合约甚至可以通过特殊的指令来创建其他合约(并不是简单地调用零地址)。这种创建合约的消息调用和普通消息调用的区别在于,负载会被执行并且执行结果会被存储为合约代码,同时将新合约地址返回给调用者。 50 51 智能合约代码从区块链上移除的唯一方式是合约在合约地址上执行自毁操作selfdestruct。存储在合约上的以太币会发送给指定账户,然后从状态中移除存储和代码。尽管一个合约没有显式地调用selfdestruct,它依然可以通过delegatecall或callcode来间接地执行自毁操作。 52 53 咳咳…连蒙带猜加上谷译的助攻终于将官档看完了,以上都是个人所读仅供参考以达抛砖引玉之用,大佬们深入理解一切还是要以官档原文为准的。 54 55 # 源码撸起来 56 57 ### 高屋建瓴总览大局 58 59 了解几本原理后,就可以从源码入手来分析下EVM的运行机制。首先来看看源码vm相关的目录结构: 60 61 ``` 62 ➜ vm pwd 63 /Users/chaors/BlockChain/ethereum/SourceCodeRead/go-ethereum-master_read/core/vm 64 ➜ vm tree 65 . 66 |____memory.go //EVM内存 67 |____opcodes.go //op指令集 68 |____analysis.go //跳转目标判断 69 |____gas_table_test.go 70 |____gas_table.go //指令耗费gas计算表 71 |____evm.go //evm对外接口 72 |____gas.go //gas花费计算 73 |____intpool_test.go 74 |____logger.go //evm日志 75 |____int_pool_verifier_empty.go 76 |____runtime 77 | |____env.go //执行环境 78 | |____runtime.go //运行时 79 | |____runtime_example_test.go 80 | |____doc.go 81 | |____runtime_test.go 82 | |____fuzz.go 83 |____interface.go 84 |____analysis_test.go 85 |____instructions.go //指令集实现 86 |____gen_structlog.go 87 |____contracts.go //预编译的合约 88 |____memory_table.go //evm内存操作表 89 |____noop.go 90 |____instructions_test.go 91 |____doc.go 92 |____stack.go //栈 93 |____common.go //一些共有方法 94 |____stack_table.go //栈验证表 95 |____interpreter.go //解释器 96 |____intpool.go //int值存储池 97 |____jump_table.go //指令和指令操作对应表 98 |____contract.go //智能合约 99 |____int_pool_verifier.go 100 |____contracts_test.go 101 |____logger_test.go 102 |____errors.go //错误类 103 ``` 104 105 ### EVM结构 106 107 ``` 108 type Context struct { 109 // CanTransfer returns whether the account contains 110 // sufficient ether to transfer the value 111 // 返回账户是否包含足够的用来传输的以太币 112 CanTransfer CanTransferFunc 113 // Transfer transfers ether from one account to the other 114 // 将以太从一个帐户转移到另一个帐户 115 Transfer TransferFunc 116 // GetHash returns the hash corresponding to n 117 GetHash GetHashFunc 118 119 // Message information 120 // 消息相关信息 121 Origin common.Address // Provides information for ORIGIN 122 GasPrice *big.Int // Provides information for GASPRICE 123 124 // Block information 125 // 区块相关信息 126 Coinbase common.Address // Provides information for COINBASE 127 GasLimit uint64 // Provides information for GASLIMIT 128 BlockNumber *big.Int // Provides information for NUMBER 129 Time *big.Int // Provides information for TIME 130 Difficulty *big.Int // Provides information for DIFFICULTY 131 } 132 133 // EVM is the Ethereum Virtual Machine base object and provides 134 // the necessary tools to run a contract on the given state with 135 // the provided context. It should be noted that any error 136 // generated through any of the calls should be considered a 137 // revert-state-and-consume-all-gas operation, no checks on 138 // specific errors should ever be performed. The interpreter makes 139 // sure that any errors generated are to be considered faulty code. 140 // 141 // The EVM should never be reused and is not thread safe. 142 // // EVM是以太坊虚拟机基础对象,并提供必要的工具,以使用提供的上下文运行给定状态的合约。 143 // 应该指出的是,任何调用产生的任何错误都应该被认为是一种回滚修改状态和消耗所有GAS操作, 144 // 不应该执行对具体错误的检查。 解释器确保生成的任何错误都被认为是错误的代码。 145 type EVM struct { 146 // Context provides auxiliary blockchain related information 147 // 辅助信息对象(包括GasPrice,GasLimit,BlockNumber等信息) 148 Context 149 // StateDB gives access to the underlying state 150 // 为EVM提供StateDB相关操作 151 StateDB StateDB 152 // Depth is the current call stack 153 // 当前调用的栈 154 depth int 155 156 // chainConfig contains information about the current chain 157 // 链配置信息 158 chainConfig *params.ChainConfig 159 // chain rules contains the chain rules for the current epoch 160 // 链规则 161 chainRules params.Rules 162 // virtual machine configuration options used to initialise the 163 // evm. 164 // 虚拟机配置 165 vmConfig Config 166 // global (to this context) ethereum virtual machine 167 // used throughout the execution of the tx. 168 // 解释器 169 interpreter *Interpreter 170 // abort is used to abort the EVM calling operations 171 // NOTE: must be set atomically 172 // 用于中止EVM调用操作 173 abort int32 174 // callGasTemp holds the gas available for the current call. This is needed because the 175 // available gas is calculated in gasCall* according to the 63/64 rule and later 176 // applied in opCall*. 177 // 当前call可用的gas 178 callGasTemp uint64 179 } 180 181 // NewEVM returns a new EVM. The returned EVM is not thread safe and should 182 // only ever be used *once*. 183 func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { 184 evm := &EVM{ 185 Context: ctx, 186 StateDB: statedb, 187 vmConfig: vmConfig, 188 chainConfig: chainConfig, 189 chainRules: chainConfig.Rules(ctx.BlockNumber), 190 } 191 192 evm.interpreter = NewInterpreter(evm, vmConfig) 193 return evm 194 } 195 ``` 196 197 ### Contract结构 198 既然EVM最直接的功能就是运行智能合约,接下来就看看智能合约的数据结构。 199 200 ``` 201 // Contract represents an ethereum contract in the state database. It contains 202 // the the contract code, calling arguments. Contract implements ContractRef 203 // 数据库中的以太坊智能合约,包括合约代码和调用参数 204 type Contract struct { 205 // CallerAddress is the result of the caller which initialised this 206 // contract. However when the "call method" is delegated this value 207 // needs to be initialised to that of the caller's caller. 208 // 合约调用者 209 CallerAddress common.Address 210 caller ContractRef 211 self ContractRef 212 213 // JUMPDEST分析的结果 214 jumpdests destinations // result of JUMPDEST analysis. 215 216 // 合约代码 217 Code []byte 218 CodeHash common.Hash 219 // 合约地址 220 CodeAddr *common.Address 221 Input []byte 222 223 Gas uint64 224 value *big.Int 225 226 Args []byte 227 228 // 是否委托调用 229 DelegateCall bool 230 } 231 232 // NewContract returns a new contract environment for the execution of EVM. 233 // 为EVM创建合约环境 234 func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { 235 c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} 236 237 if parent, ok := caller.(*Contract); ok { 238 // Reuse JUMPDEST analysis from parent context if available. 239 c.jumpdests = parent.jumpdests 240 } else { 241 c.jumpdests = make(destinations) 242 } 243 244 // Gas should be a pointer so it can safely be reduced through the run 245 // This pointer will be off the state transition 246 c.Gas = gas 247 // ensures a value is set 248 c.value = value 249 250 return c 251 } 252 ``` 253 254 ### EVM工作逻辑 255 256 EVM运行的大概逻辑是这样的: 257 258 - __1.创建EVM运行的上下文环境,同时实例化一个EVM对象__ 259 260 - __2.合约不存在则创建新合约,使用已经存在的合约则世界调用call__ 261 262 - __3.EVM通过interpreter解释器来执行智能合约__ 263 264 创建EVM对象的代码: 265 266 ``` 267 // NewEVMContext creates a new context for use in the EVM. 268 // 1.创建EVM上下文环境 269 func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { 270 // If we don't have an explicit author (i.e. not mining), extract from the header 271 // 如果不挖矿,受益人从区块头中提取 272 var beneficiary common.Address 273 if author == nil { 274 beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation 275 } else { 276 beneficiary = *author 277 } 278 return vm.Context{ 279 CanTransfer: CanTransfer, 280 Transfer: Transfer, 281 GetHash: GetHashFn(header, chain), 282 Origin: msg.From(), 283 Coinbase: beneficiary, 284 BlockNumber: new(big.Int).Set(header.Number), 285 Time: new(big.Int).Set(header.Time), 286 Difficulty: new(big.Int).Set(header.Difficulty), 287 GasLimit: header.GasLimit, 288 GasPrice: new(big.Int).Set(msg.GasPrice()), 289 } 290 } 291 ... 292 // NewEVM returns a new EVM. The returned EVM is not thread safe and should 293 // only ever be used *once*. 294 // 2.创建EVM对象 295 func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { 296 evm := &EVM{ 297 Context: ctx, 298 StateDB: statedb, 299 vmConfig: vmConfig, 300 chainConfig: chainConfig, 301 chainRules: chainConfig.Rules(ctx.BlockNumber), 302 } 303 304 // 3.创建EVM解释器 305 evm.interpreter = NewInterpreter(evm, vmConfig) 306 return evm 307 } 308 ... 309 // NewInterpreter returns a new instance of the Interpreter. 310 // 3.创建解释器 311 func NewInterpreter(evm *EVM, cfg Config) *Interpreter { 312 // We use the STOP instruction whether to see 313 // the jump table was initialised. If it was not 314 // we'll set the default jump table. 315 if !cfg.JumpTable[STOP].valid { 316 switch { 317 case evm.ChainConfig().IsConstantinople(evm.BlockNumber): 318 cfg.JumpTable = constantinopleInstructionSet 319 case evm.ChainConfig().IsByzantium(evm.BlockNumber): 320 cfg.JumpTable = byzantiumInstructionSet 321 case evm.ChainConfig().IsHomestead(evm.BlockNumber): 322 cfg.JumpTable = homesteadInstructionSet 323 default: 324 cfg.JumpTable = frontierInstructionSet 325 } 326 } 327 328 return &Interpreter{ 329 evm: evm, 330 cfg: cfg, 331 gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), 332 } 333 } 334 ``` 335 336 创建合约的代码 337 338 ``` 339 // Create creates a new contract using code as deployment code. 340 // 创建合约 341 func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { 342 343 // Depth check execution. Fail if we're trying to execute above the 344 // limit. 345 // 执行深度检查,如果超出设定的深度限制 创建失败 346 if evm.depth > int(params.CallCreateDepth) { 347 return nil, common.Address{}, gas, ErrDepth 348 } 349 // 账户余额不足,创建失败 350 if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { 351 return nil, common.Address{}, gas, ErrInsufficientBalance 352 } 353 // Ensure there's no existing contract already at the designated address 354 // 确保指定地址没有已存在的相同合约 355 nonce := evm.StateDB.GetNonce(caller.Address()) 356 evm.StateDB.SetNonce(caller.Address(), nonce+1) 357 358 // 创建合约地址 359 contractAddr = crypto.CreateAddress(caller.Address(), nonce) 360 contractHash := evm.StateDB.GetCodeHash(contractAddr) 361 if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { 362 return nil, common.Address{}, 0, ErrContractAddressCollision 363 } 364 // Create a new account on the state 365 // 创建数据库快照,为了迅速回滚 366 snapshot := evm.StateDB.Snapshot() 367 // 在当前状态新建合约账户 368 evm.StateDB.CreateAccount(contractAddr) 369 if evm.ChainConfig().IsEIP158(evm.BlockNumber) { 370 evm.StateDB.SetNonce(contractAddr, 1) 371 } 372 // 转账操作 373 evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value) 374 375 // initialise a new contract and set the code that is to be used by the 376 // EVM. The contract is a scoped environment for this execution context 377 // only. 378 // 创建合约 379 contract := NewContract(caller, AccountRef(contractAddr), value, gas) 380 // 设置合约代码 381 contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) 382 383 if evm.vmConfig.NoRecursion && evm.depth > 0 { 384 return nil, contractAddr, gas, nil 385 } 386 387 if evm.vmConfig.Debug && evm.depth == 0 { 388 evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value) 389 } 390 start := time.Now() 391 392 // 执行合约的初始化 393 ret, err = run(evm, contract, nil) 394 395 // check whether the max code size has been exceeded 396 // 检查初始化生成的代码长度是否超过限制 397 maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize 398 // if the contract creation ran successfully and no errors were returned 399 // calculate the gas required to store the code. If the code could not 400 // be stored due to not enough gas set an error and let it be handled 401 // by the error checking condition below. 402 // 合约创建成功 403 if err == nil && !maxCodeSizeExceeded { 404 // 计算存储代码所需要的Gas 405 createDataGas := uint64(len(ret)) * params.CreateDataGas 406 if contract.UseGas(createDataGas) { 407 evm.StateDB.SetCode(contractAddr, ret) 408 } else { 409 // 当前拥有的Gas不足以存储代码 410 err = ErrCodeStoreOutOfGas 411 } 412 } 413 414 // When an error was returned by the EVM or when setting the creation code 415 // above we revert to the snapshot and consume any gas remaining. Additionally 416 // when we're in homestead this also counts for code storage gas errors. 417 // 合约创建失败,借助上面创建的快照快速回滚 418 if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { 419 evm.StateDB.RevertToSnapshot(snapshot) 420 if err != errExecutionReverted { 421 contract.UseGas(contract.Gas) 422 } 423 } 424 // Assign err if contract code size exceeds the max while the err is still empty. 425 if maxCodeSizeExceeded && err == nil { 426 err = errMaxCodeSizeExceeded 427 } 428 if evm.vmConfig.Debug && evm.depth == 0 { 429 evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) 430 } 431 return ret, contractAddr, contract.Gas, err 432 } 433 ... 434 // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. 435 func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { 436 if contract.CodeAddr != nil { 437 precompiles := PrecompiledContractsHomestead 438 if evm.ChainConfig().IsByzantium(evm.BlockNumber) { 439 precompiles = PrecompiledContractsByzantium 440 } 441 if p := precompiles[*contract.CodeAddr]; p != nil { 442 // 运行预编译合约 443 return RunPrecompiledContract(p, input, contract) 444 } 445 } 446 // 解释器执行合约代码 447 return evm.interpreter.Run(contract, input) 448 } 449 ``` 450 451 这里合约代码的执行后续再看,当调用已创建的合约时,使用的是call方法。Call方法和create方法的逻辑大体相同,这里分析下他们的不同之处: 452 453 - 1.call调用的是一个已经存在合约账户的合约,create是新建一个合约账户。 454 455 - 2.call里evm.Transfer发生在合约的发送方和接收方,create里则是创建合约用户的账户和该合约用户之间。 456 457 ``` 458 // Call executes the contract associated with the addr with the given input as 459 // parameters. It also handles any necessary value transfer required and takes 460 // the necessary steps to create accounts and reverses the state in case of an 461 // execution error or failed value transfer. 462 // 使用给定输入作为参数执行与addr关联的合约 463 func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { 464 if evm.vmConfig.NoRecursion && evm.depth > 0 { 465 return nil, gas, nil 466 } 467 468 // Fail if we're trying to execute above the call depth limit 469 if evm.depth > int(params.CallCreateDepth) { 470 return nil, gas, ErrDepth 471 } 472 // Fail if we're trying to transfer more than the available balance 473 if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { 474 return nil, gas, ErrInsufficientBalance 475 } 476 477 var ( 478 to = AccountRef(addr) 479 snapshot = evm.StateDB.Snapshot() 480 ) 481 if !evm.StateDB.Exist(addr) { 482 precompiles := PrecompiledContractsHomestead 483 if evm.ChainConfig().IsByzantium(evm.BlockNumber) { 484 precompiles = PrecompiledContractsByzantium 485 } 486 if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { 487 // Calling a non existing account, don't do antything, but ping the tracer 488 if evm.vmConfig.Debug && evm.depth == 0 { 489 evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) 490 evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) 491 } 492 return nil, gas, nil 493 } 494 evm.StateDB.CreateAccount(addr) 495 } 496 evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) 497 498 // Initialise a new contract and set the code that is to be used by the EVM. 499 // The contract is a scoped environment for this execution context only. 500 contract := NewContract(caller, to, value, gas) 501 contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) 502 503 start := time.Now() 504 505 // Capture the tracer start/end events in debug mode 506 if evm.vmConfig.Debug && evm.depth == 0 { 507 evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) 508 509 defer func() { // Lazy evaluation of the parameters 510 evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) 511 }() 512 } 513 ret, err = run(evm, contract, input) 514 515 // When an error was returned by the EVM or when setting the creation code 516 // above we revert to the snapshot and consume any gas remaining. Additionally 517 // when we're in homestead this also counts for code storage gas errors. 518 if err != nil { 519 evm.StateDB.RevertToSnapshot(snapshot) 520 if err != errExecutionReverted { 521 contract.UseGas(contract.Gas) 522 } 523 } 524 return ret, contract.Gas, err 525 } 526 ``` 527 528 ### DelegateCall 529 530 上面阅读官档时,涉及到一个DelegateCall委托调用的概念。上面看的Call函数便是便是普通的消息调用,接下来看看EVM中几个特殊的消息调用。这里只讲它们的特殊之处,代码逻辑和Call大体相同,源码就不再看了,参考Call即可。 531 532 - CallCode,它与Call不同的地方在于它使用调用者的EVMContext来执行给定地址的合约代码。 533 534 - DelegateCall,它与CallCode不同的地方在于它调用者被设置为调用者的调用者 535 536 - StaticCall,它不允许执行任何状态的修改 537 538 - 以上三个特殊的消息调用只能由opcode触发,它们不像Call可以由外部调用。 539 540 ### Interpreter EVM解释器 541 542 合约的执行最终是靠解释器Interpreter来实现的,这里就来看看Interpreter的数据结构。 543 544 ``` 545 // Config are the configuration options for the Interpreter 546 // 解释器配置类 547 type Config struct { 548 // Debug enabled debugging Interpreter options 549 // 启用调试 550 Debug bool 551 // Tracer is the op code logger 552 // 操作码记录器 553 Tracer Tracer 554 // NoRecursion disabled Interpreter call, callcode, 555 // delegate call and create. 556 // 禁用解释器调用,代码库调用,委托调用 557 NoRecursion bool 558 // Enable recording of SHA3/keccak preimages 559 // 启用SHA3/keccak 560 EnablePreimageRecording bool 561 // JumpTable contains the EVM instruction table. This 562 // may be left uninitialised and will be set to the default 563 // table. 564 // 操作码opcode对应的操作表 565 JumpTable [256]operation 566 } 567 568 // Interpreter is used to run Ethereum based contracts and will utilise the 569 // passed environment to query external sources for state information. 570 // The Interpreter will run the byte code VM based on the passed 571 // configuration. 572 // 用来运行智能合约的字节码 573 type Interpreter struct { 574 evm *EVM 575 // 解释器配置 576 cfg Config 577 // gas价格表,根据不同的以太坊阶段来决定 578 gasTable params.GasTable 579 intPool *intPool 580 581 readOnly bool // Whether to throw on stateful modifications 582 // 最后一个call调用的返回值 583 returnData []byte // Last CALL's return data for subsequent reuse 584 } 585 ``` 586 587 接着继续看它是怎么实现智能合约的执行的。 588 589 ``` 590 // Run loops and evaluates the contract's code with the given input data and returns 591 // the return byte-slice and an error if one occurred. 592 // 593 // It's important to note that any errors returned by the interpreter should be 594 // considered a revert-and-consume-all-gas operation except for 595 // errExecutionReverted which means revert-and-keep-gas-left. 596 // 执行合约代码 597 func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { 598 if in.intPool == nil { 599 in.intPool = poolOfIntPools.get() 600 defer func() { 601 poolOfIntPools.put(in.intPool) 602 in.intPool = nil 603 }() 604 } 605 606 // Increment the call depth which is restricted to 1024 607 // 调用深度递增,evm执行栈的深度不能超过1024 608 in.evm.depth++ 609 defer func() { in.evm.depth-- }() 610 611 // Reset the previous call's return data. It's unimportant to preserve the old buffer 612 // as every returning call will return new data anyway. 613 // 重置上一个call的返回数据 614 in.returnData = nil 615 616 // Don't bother with the execution if there's no code. 617 // 合约代码为空 618 if len(contract.Code) == 0 { 619 return nil, nil 620 } 621 622 var ( 623 op OpCode // current opcode 624 mem = NewMemory() // bound memory 625 stack = newstack() // local stack 626 // For optimisation reason we're using uint64 as the program counter. 627 // It's theoretically possible to go above 2^64. The YP defines the PC 628 // to be uint256. Practically much less so feasible. 629 pc = uint64(0) // program counter 630 cost uint64 631 // copies used by tracer 632 pcCopy uint64 // needed for the deferred Tracer 633 gasCopy uint64 // for Tracer to log gas remaining before execution 634 logged bool // deferred Tracer should ignore already logged steps 635 ) 636 contract.Input = input 637 638 // Reclaim the stack as an int pool when the execution stops 639 // 执行停止时将栈回收为int值缓存池 640 defer func() { in.intPool.put(stack.data...) }() 641 642 if in.cfg.Debug { 643 defer func() { 644 if err != nil { 645 if !logged { 646 in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 647 } else { 648 in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 649 } 650 } 651 }() 652 } 653 // The Interpreter main run loop (contextual). This loop runs until either an 654 // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during 655 // the execution of one of the operations or until the done flag is set by the 656 // parent context. 657 // 解释器主循环,循环运行直到执行显式STOP,RETURN或SELFDESTRUCT,发生错误 658 for atomic.LoadInt32(&in.evm.abort) == 0 { 659 if in.cfg.Debug { 660 // Capture pre-execution values for tracing. 661 // 捕获预执行的值进行跟踪 662 logged, pcCopy, gasCopy = false, pc, contract.Gas 663 } 664 665 // Get the operation from the jump table and validate the stack to ensure there are 666 // enough stack items available to perform the operation. 667 // 从合约的二进制数据i获取第pc个opcode操作符 opcode是以太坊虚拟机指令,一共不超过256个,正好一个byte大小能装下 668 op = contract.GetOp(pc) 669 // 从JumpTable表中查询op对应的操作 670 operation := in.cfg.JumpTable[op] 671 if !operation.valid { 672 return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) 673 } 674 if err := operation.validateStack(stack); err != nil { 675 return nil, err 676 } 677 // If the operation is valid, enforce and write restrictions 678 // 操作有效,强制执行 679 if err := in.enforceRestrictions(op, operation, stack); err != nil { 680 return nil, err 681 } 682 683 var memorySize uint64 684 // calculate the new memory size and expand the memory to fit 685 // the operation 686 // 计算新的内存大小以适应操作,必要时进行扩容 687 if operation.memorySize != nil { 688 // memSize不能大于64位 689 memSize, overflow := bigUint64(operation.memorySize(stack)) 690 if overflow { 691 return nil, errGasUintOverflow 692 } 693 // memory is expanded in words of 32 bytes. Gas 694 // is also calculated in words. 695 // 扩容按32字节的字扩展 696 if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { 697 return nil, errGasUintOverflow 698 } 699 } 700 // consume the gas and return an error if not enough gas is available. 701 // cost is explicitly set so that the capture state defer method can get the proper cost 702 // 计算执行操作所需要的gas 703 cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) 704 // gas不足 705 if err != nil || !contract.UseGas(cost) { 706 return nil, ErrOutOfGas 707 } 708 if memorySize > 0 { 709 mem.Resize(memorySize) 710 } 711 712 if in.cfg.Debug { 713 in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 714 logged = true 715 } 716 717 // execute the operation 718 // 执行操作 719 res, err := operation.execute(&pc, in.evm, contract, mem, stack) 720 // verifyPool is a build flag. Pool verification makes sure the integrity 721 // of the integer pool by comparing values to a default value. 722 // 验证int值缓存池 723 if verifyPool { 724 verifyIntegerPool(in.intPool) 725 } 726 // if the operation clears the return data (e.g. it has returning data) 727 // set the last return to the result of the operation. 728 // 将最后一次返回设为操作结果 729 if operation.returns { 730 in.returnData = res 731 } 732 733 switch { 734 case err != nil: 735 return nil, err 736 case operation.reverts: 737 return res, errExecutionReverted 738 case operation.halts: 739 return res, nil 740 case !operation.jumps: 741 pc++ 742 } 743 } 744 return nil, nil 745 } 746 ``` 747 748 ### JumpTable(opCode-operation) 749 750 在执行合约的时候涉及到contract.GetOp(pc)方法从合约二进制代码中取出第pc个操作符opcode,然后再按对应关系找到opcode对应的操作operation。这里的对应关系就保存在jump_table中。 751 752 这里先要理解操作符opcode的概念,它是EVM的操作符。通俗地讲,一个opcode就是一个byte,solidity合约编译形成的bytecode中,一个byte就代表一个opcode。opcodes.go中定义了所有的操作符,并将所有的操作符按功能分类。例如下面是一组块操作相关的操作符: 753 754 ``` 755 // 0x40 range - block operations. 756 const ( 757 BLOCKHASH OpCode = 0x40 + iota 758 COINBASE 759 TIMESTAMP 760 NUMBER 761 DIFFICULTY 762 GASLIMIT 763 ) 764 ``` 765 766 每一个opcode都会对应一个具体的操作operation,一个操作包含其操作函数以及一些必要的参数。 767 768 ``` 769 type operation struct { 770 // execute is the operation function 771 // 操作函数 772 execute executionFunc 773 // gasCost is the gas function and returns the gas required for execution 774 // 计算操作需要多少gas的函数 775 gasCost gasFunc 776 // validateStack validates the stack (size) for the operation 777 // 验证操作的栈 778 validateStack stackValidationFunc 779 // memorySize returns the memory size required for the operation 780 // 操作需要的内存大小 781 memorySize memorySizeFunc 782 783 // 操作终止 784 halts bool // indicates whether the operation should halt further execution 785 // 操作跳转 786 jumps bool // indicates whether the program counter should not increment 787 // 是否写入 788 writes bool // determines whether this a state modifying operation 789 // 操作是否有效 790 valid bool // indication whether the retrieved operation is valid and known 791 // 出错回滚 792 reverts bool // determines whether the operation reverts state (implicitly halts) 793 // 操作返回 794 returns bool // determines whether the operations sets the return data content 795 } 796 ``` 797 798 opcode和operation的对应关系都在jump_table.go中。例如我们上面举例的相关块操作的操作符,这里以EXTCODECOPY(0x3d)操作符为例: 799 800 ``` 801 EXTCODECOPY: { 802 execute: opExtCodeCopy, 803 gasCost: gasExtCodeCopy, 804 validateStack: makeStackFunc(4, 0), 805 memorySize: memoryExtCodeCopy, 806 valid: true, 807 }, 808 ``` 809 810 针对每一个具体的操作operation,其内部属性对应的实现代码为: 811 812 - execute---instructions.go,例如上面里的opExtCodeCopy 813 814 - gasCost---gas_table.go, 例如上面里的gasExtCodeCopy 815 816 - validateStack---stack_table,例如上面里的makeStackFunc 817 818 - memorySize---memory_table.go,例如上面里的memoryExtCodeCopy 819 820 ### Stack栈 821 822 EVM是基于栈的虚拟机,这里栈的作用是用来保存操作数的。 823 824 ``` 825 // Stack is an object for basic stack operations. Items popped to the stack are 826 // expected to be changed and modified. stack does not take care of adding newly 827 // initialised objects. 828 type Stack struct { 829 data []*big.Int 830 } 831 832 func newstack() *Stack { 833 return &Stack{data: make([]*big.Int, 0, 1024)} 834 } 835 836 func (st *Stack) push(d *big.Int) { 837 // NOTE push limit (1024) is checked in baseCheck 838 //stackItem := new(big.Int).Set(d) 839 //st.data = append(st.data, stackItem) 840 st.data = append(st.data, d) 841 } 842 843 func (st *Stack) pop() (ret *big.Int) { 844 ret = st.data[len(st.data)-1] 845 st.data = st.data[:len(st.data)-1] 846 return 847 } 848 ``` 849 850 ### Memory & stateDB 851 852 Memery类为EVM实现了一个简单的内存模型。它主要在执行合约时针对operation进行一些内存里的参数拷贝。 853 854 ``` 855 // Memory implements a simple memory model for the ethereum virtual machine. 856 type Memory struct { 857 // 内存 858 store []byte 859 // 最后一次的gas花费 860 lastGasCost uint64 861 } 862 863 // NewMemory returns a new memory memory model. 864 func NewMemory() *Memory { 865 return &Memory{} 866 } 867 ``` 868 869 前面在创建合约账户的时候,将合约代码存储到了数据库。当创建合约失败的时候,也是利用数据库快照进行回滚状态的。 870 871 ### 872 873 当有这样一段智能合约代码: 874 875 ``` 876 pragma solidity ^0.4.0; 877 contract SimpleStorage { 878 uint storedData; 879 880 function set(uint x) public { 881 storedData = x; 882 } 883 884 function get() public returns (uint) { 885 return storedData; 886 } 887 } 888 ``` 889 890 在Remix编译器进行编译后得到字节码: 891 892 ``` 893 { 894 "object": "606060405260a18060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360fe47b11460435780636d4ce63c14605d57603f565b6002565b34600257605b60048080359060200190919050506082565b005b34600257606c60048050506090565b6040518082815260200191505060405180910390f35b806000600050819055505b50565b60006000600050549050609e565b9056", 895 "opcodes": "PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0xA1 DUP1 PUSH1 0x10 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x43 JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH1 0x5D JUMPI PUSH1 0x3F JUMP JUMPDEST PUSH1 0x2 JUMP JUMPDEST CALLVALUE PUSH1 0x2 JUMPI PUSH1 0x5B PUSH1 0x4 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP2 SWAP1 POP POP PUSH1 0x82 JUMP JUMPDEST STOP JUMPDEST CALLVALUE PUSH1 0x2 JUMPI PUSH1 0x6C PUSH1 0x4 DUP1 POP POP PUSH1 0x90 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 PUSH1 0x0 PUSH1 0x0 POP DUP2 SWAP1 SSTORE POP JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 POP SLOAD SWAP1 POP PUSH1 0x9E JUMP JUMPDEST SWAP1 JUMP ", 896 "sourceMap": "24:189:0:-;;;;;;;;;", 897 "linkReferences": {} 898 } 899 ``` 900 901 其中,opcodes字段便是合约代码编译后的操作码集合。 902 903 以PUSH1 0x60为例,可以在jump_table.go中找到对应的operation: 904 905 ``` 906 PUSH1: { 907 execute: makePush(1, 1), 908 gasCost: gasPush, 909 validateStack: makeStackFunc(0, 1), 910 valid: true, 911 } 912 ``` 913 914 此时EVM就会去执行makePush函数,同时通过gasPush计算该操作需要的gas费用。EVM内部通过pop不断进行出栈操作来处理整个操作码集,当栈为空的时候表示整个合约代码执行完毕得到最后的执行结果。 915 916 至此,有关EVM的源码研读就告一段落了。 917 918